package com.haohan.cloud.scm.saleb.core.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.haohan.cloud.scm.api.constant.NumberPrefixConstant;
import com.haohan.cloud.scm.api.constant.enums.common.DeliveryTypeEnum;
import com.haohan.cloud.scm.api.constant.enums.opc.BuySeqEnum;
import com.haohan.cloud.scm.api.constant.enums.saleb.BuyOrderStatusEnum;
import com.haohan.cloud.scm.api.constant.enums.saleb.DetailSummaryFlagEnum;
import com.haohan.cloud.scm.api.goods.dto.GoodsModelDTO;
import com.haohan.cloud.scm.api.manage.dto.ShopExtDTO;
import com.haohan.cloud.scm.api.saleb.dto.BuyOrderImport;
import com.haohan.cloud.scm.api.saleb.dto.BuyOrderImportDTO;
import com.haohan.cloud.scm.api.saleb.entity.BuyOrder;
import com.haohan.cloud.scm.api.saleb.entity.BuyOrderDetail;
import com.haohan.cloud.scm.api.saleb.entity.Buyer;
import com.haohan.cloud.scm.api.saleb.req.BuyOrderImportOneReq;
import com.haohan.cloud.scm.common.tools.util.ScmIncrementUtil;
import com.haohan.cloud.scm.common.tools.util.ValidatorUtil;
import com.haohan.cloud.scm.saleb.core.BuyOrderImportCoreService;
import com.haohan.cloud.scm.saleb.service.BuyOrderDetailService;
import com.haohan.cloud.scm.saleb.service.BuyOrderService;
import com.haohan.cloud.scm.saleb.service.BuyerService;
import com.haohan.cloud.scm.saleb.utils.ScmSaleBUtils;
import com.pig4cloud.pigx.common.core.util.R;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author dy
 * @date 2019/9/13
 */
@Service
@AllArgsConstructor
public class BuyOrderImportCoreServiceImpl implements BuyOrderImportCoreService {

    private final ScmIncrementUtil scmIncrementUtil;
    private final BuyOrderService buyOrderService;
    private final BuyOrderDetailService buyOrderDetailService;
    private final BuyerService buyerService;
    private final ScmSaleBUtils scmSaleBUtils;

    /**
     * 处理导入的采购单信息列表  多个采购单
     *
     * @param list
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> saveBuyOrder(List<BuyOrderImport> list) {
        StringBuilder msg = new StringBuilder();
        // 查询平台商家店铺
        ShopExtDTO shop = scmSaleBUtils.fetchPurchaseShop("");
        if (null == shop) {
            msg.append("未设置平台商家店铺, 请更正后重新导入。");
            return R.failed(msg.toString());
        }
        String shopId = shop.getId();
        int size = list.size() * 4 / 3 + 1;
        size = Math.max(8, size);
        Map<String, GoodsModelDTO> modelMap = new HashMap<>(size);
        Map<String, BuyOrderImportDTO> orderMap = new HashMap<>(size);
        Map<String, Buyer> buyerMap = new HashMap<>(16);
        // 处理每行
        int i = 1;
        int errorNum = 0;
        int successNum = 0;
        for (BuyOrderImport rowInfo : list) {
            i++;
            // 验证是否空行， 是否填入必须属性;  1 通过  0 空行 -1 缺少必填属性
            R result = checkInfo(rowInfo);
            int flag = result.getCode();
            if (flag == 0) {
                continue;
            }
            if (flag == -1) {
                msg.append("第").append(i).append("行填写有误:").append(result.getMsg()).append("|");
                errorNum++;
                continue;
            }
            if (!initBuyOrderDetail(shopId, rowInfo, modelMap, orderMap, buyerMap)) {
                msg.append("第").append(i).append("行采购商有误|");
                errorNum++;
            } else {
                successNum++;
            }

        }
        // 保存采购单
        if (errorNum == 0) {
            for (BuyOrderImportDTO b : orderMap.values()) {
                saveImportOrder(b);
            }
            msg.append("本次共导入采购单").append(orderMap.values().size())
                    .append("笔;共有商品采购明细").append(successNum).append("条记录。");
            return R.ok(msg.toString(), "success");
        }
        msg.append("请更正后重新导入。");
        return R.failed(msg.toString());
    }

    /**
     * 验证是否空行， 是否填入必须属性;  1 通过  0 空行 -1 缺少必填属性
     *
     * @param rowInfo
     * @return
     */
    private R<String> checkInfo(BuyOrderImport rowInfo) {
        // 空行
        boolean string = StrUtil.isAllBlank(rowInfo.getBuySeq(), rowInfo.getBuyerName(),
                rowInfo.getGoodsName(), rowInfo.getGoodsModel());
        boolean price = null == rowInfo.getDeliveryTime() && null == rowInfo.getBuyPrice() && null == rowInfo.getOrderGoodsNum();
        if (string && price) {
            return new R<>(0, "空行", null);
        }
        // 验证通过
        String msg = ValidatorUtil.validate(rowInfo, false);
        if (StrUtil.isEmpty(msg)) {
            return new R<>(1, "验证通过", null);
        }
        // 验证不通过
        return new R<>(-1, msg, null);
    }

    /**
     * 保存导入的采购单
     *
     * @param buyOrderImportDTO
     * @return
     */
    private BuyOrder saveImportOrder(BuyOrderImportDTO buyOrderImportDTO) {
        String buyId = scmIncrementUtil.inrcSnByClass(BuyOrder.class, NumberPrefixConstant.BUY_ORDER_SN_PRE);
        BuyOrder buyOrder = new BuyOrder();
        BeanUtil.copyProperties(buyOrderImportDTO, buyOrder);
        buyOrder.setBuyId(buyId);

        // 保存明细  计算金额
        BigDecimal total = buyOrderImportDTO.getShipFee();
        total = (null == total) ? BigDecimal.ZERO : total;

        List<BuyOrderDetail> detailList = buyOrderImportDTO.getDetailList();
        int size = detailList.size() * 4 / 3 + 1;
        size = Math.max(size, 8);
        // 商品去重
        Map<String, BuyOrderDetail> detailMap = new HashMap<>(size);

        total = saveBuyOrderDetail(buyOrder, total, detailList, detailMap);

        buyOrder.setGenPrice(total);
        buyOrder.setTotalPrice(total);
        buyOrder.setStatus(BuyOrderStatusEnum.submit);
        buyOrderService.save(buyOrder);
        return buyOrder;
    }

    /**
     * 导入的一行转为 商品明细
     * 若无商品信息,  新增商品(下架状态, 分类为  未确认商品-> 导入商品)
     *
     * @param rowInfo  必须值: buyerName|deliveryTime|buySeq
     * @param modelMap 商品名称|规格名称 : goodsModelDTO
     * @param orderMap buyerId|deliveryTime|buySeq : BuyOrderImportDTO
     * @param buyerMap buyerName : buyer
     * @return
     */
    private boolean initBuyOrderDetail(String shopId, BuyOrderImport rowInfo, Map<String, GoodsModelDTO> modelMap, Map<String, BuyOrderImportDTO> orderMap, Map<String, Buyer> buyerMap) {
        // 采购商 不存在时 不继续
        Buyer buyer;
        String buyerName = rowInfo.getBuyerName();
        if (buyerMap.containsKey(buyerName)) {
            buyer = buyerMap.get(buyerName);
        } else {
            buyer = buyerService.fetchByName(rowInfo.getBuyerName());
            if (null == buyer) {
                return false;
            }
            buyerMap.put(buyerName, buyer);
        }
        // 时间批次处理
        LocalDate deliveryDate = rowInfo.getDeliveryTime().toLocalDate();
        BuySeqEnum buySeq = BuySeqEnum.getByType(rowInfo.getBuySeq());
        buySeq = (null == buySeq) ? BuySeqEnum.first : buySeq;
        // orderMap 键值
        String orderKey = buyer.getId() + deliveryDate + buySeq;
        // 查找采购单, 不存在时新增至map
        BuyOrderImportDTO buyOrder;
        if (orderMap.containsKey(orderKey)) {
            buyOrder = orderMap.get(orderKey);
        } else {
            buyOrder = initBuyOrderImportDTO(rowInfo, buyer, deliveryDate, buySeq);
            orderMap.put(orderKey, buyOrder);
        }
        String modelKey = rowInfo.getGoodsName() + rowInfo.getGoodsModel();
        // 查找商品 , 不存在时新增至数据库/map
        GoodsModelDTO goodsModel;
        if (modelMap.containsKey(modelKey)) {
            goodsModel = modelMap.get(modelKey);
        } else {
            goodsModel = fetchGoodsModel(shopId, rowInfo);
            modelMap.put(modelKey, goodsModel);
        }
        // 采购明细 放入采购单
        BuyOrderDetail detail = initDetailByModel(rowInfo, goodsModel);
        buyOrder.getDetailList().add(detail);
        return true;
    }

    /**
     * 获取商品规格
     * 先根据名称查询, 若无对应商品则新增 商品(下架状态, 分类为  未确认商品-> 导入商品)
     *
     * @param shopId
     * @param rowInfo goodsName / modelName /unit /price
     * @return
     */
    private GoodsModelDTO fetchGoodsModel(String shopId, BuyOrderImport rowInfo) {
        GoodsModelDTO goodsModelDTO = new GoodsModelDTO();
        goodsModelDTO.setShopId(shopId);
        goodsModelDTO.setGoodsName(rowInfo.getGoodsName());
        goodsModelDTO.setModelName(rowInfo.getGoodsModel());
        String unit = rowInfo.getUnit();
        unit = StrUtil.isBlank(unit) ? "斤" : unit;
        goodsModelDTO.setModelUnit(unit);
        goodsModelDTO.setModelPrice(rowInfo.getBuyPrice());
        return scmSaleBUtils.fetchModelByNameOrAdd(goodsModelDTO);
    }

    /**
     * 设置 采购明细属性
     *
     * @param rowInfo
     * @param goodsModel
     * @return
     */
    private BuyOrderDetail initDetailByModel(BuyOrderImport rowInfo, GoodsModelDTO goodsModel) {
        BuyOrderDetail detail = new BuyOrderDetail();
        // 商品属性
        detail.setGoodsModelId(goodsModel.getId());
        detail.setGoodsImg(goodsModel.getModelUrl());
        detail.setGoodsName(goodsModel.getGoodsName());
        detail.setGoodsModel(goodsModel.getModelName());
        detail.setMarketPrice(goodsModel.getModelPrice());
        detail.setUnit(goodsModel.getModelUnit());
        // 明细
        detail.setGoodsNum(rowInfo.getOrderGoodsNum());
        detail.setBuyPrice(rowInfo.getBuyPrice());
        detail.setOrderGoodsNum(rowInfo.getOrderGoodsNum());
        return detail;
    }

    /**
     * 设置采购订单 属性
     *
     * @param rowInfo
     * @param buyer
     * @param deliveryDate
     * @param buySeq
     * @return
     */
    private BuyOrderImportDTO initBuyOrderImportDTO(BuyOrderImport rowInfo, Buyer buyer, LocalDate deliveryDate, BuySeqEnum buySeq) {
        BuyOrderImportDTO buyOrder = new BuyOrderImportDTO();
        buyOrder.setPmId(buyer.getPmId());
        buyOrder.setBuyerId(buyer.getId());
        buyOrder.setBuyerName(buyer.getBuyerName());
        buyOrder.setBuyerUid(buyer.getPassportId());
        buyOrder.setDeliveryTime(deliveryDate);
        buyOrder.setBuySeq(buySeq);
        buyOrder.setNeedNote(rowInfo.getNeedNote());
        // 联系人、电话、地址 处理
        buyOrder.setContact((StrUtil.isBlank(rowInfo.getContact())) ? buyer.getContact() : rowInfo.getContact());
        buyOrder.setTelephone((StrUtil.isBlank(rowInfo.getTelephone())) ? buyer.getTelephone() : rowInfo.getTelephone());
        buyOrder.setAddress((StrUtil.isBlank(rowInfo.getAddress())) ? buyer.getAddress() : rowInfo.getAddress());

        buyOrder.setBuyTime(LocalDateTime.now());
        buyOrder.setShipFee((null == rowInfo.getShipFee()) ? BigDecimal.ZERO : rowInfo.getShipFee());

        DeliveryTypeEnum deliveryType = DeliveryTypeEnum.getByType(rowInfo.getDeliveryType());
        buyOrder.setDeliveryType((null == deliveryType) ? DeliveryTypeEnum.home_delivery : deliveryType);

        buyOrder.setDetailList(new ArrayList<>());
        return buyOrder;
    }

    /**
     * 保存导入单个采购单
     *
     * @param list
     * @param req  buyerId / deliveryTime/ buySeq
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> saveOneBuyOrder(List<BuyOrderImport> list, BuyOrderImportOneReq req) {
        StringBuilder msg = new StringBuilder();
        // 查询平台商家店铺
        ShopExtDTO shop = scmSaleBUtils.fetchPurchaseShop("");
        if (null == shop) {
            msg.append("未设置平台商家店铺, 请更正后重新导入。");
            return R.failed(msg.toString());
        }
        Buyer buyer = buyerService.getById(req.getBuyerId());
        if (null == buyer) {
            msg.append("采购商有误，请更正后重新导入。");
            return R.failed(msg.toString());
        }
        String shopId = shop.getId();
        int size = list.size() * 4 / 3 + 1;
        size = Math.max(8, size);
        Map<String, GoodsModelDTO> modelMap = new HashMap<>(size);

        // 初始采购单
        BuyOrderImport buyOrderImport = new BuyOrderImport();
        buyOrderImport.setNeedNote(req.getNeedNote());
        buyOrderImport.setDeliveryType(req.getDeliveryType());
        BuyOrderImportDTO buyOrder = initBuyOrderImportDTO(buyOrderImport, buyer, req.getDeliveryTime(), req.getBuySeq());
        // 处理每行
        int i = 1;
        int errorNum = 0;
        int successNum = 0;
        LocalDateTime deliveryTime = req.getDeliveryTime().atStartOfDay();
        String buySeq = req.getBuySeq().getType();
        String buyerName = buyer.getBuyerName();
        for (BuyOrderImport rowInfo : list) {
            i++;
            // todo 修改为分组校验
            rowInfo.setDeliveryTime(deliveryTime);
            rowInfo.setBuySeq(buySeq);
            rowInfo.setBuyerName(buyerName);
            // 验证是否空行， 是否填入必须属性;  1 通过  0 空行 -1 缺少必填属性
            R result = checkInfo(rowInfo);
            int flag = result.getCode();
            if (flag == 0) {
                continue;
            }
            if (flag == -1) {
                msg.append("第").append(i).append("行填写有误:").append(result.getMsg()).append("|");
                errorNum++;
                continue;
            }

            // 查找商品 , 不存在时新增至数据库/map
            String modelKey = rowInfo.getGoodsName() + rowInfo.getGoodsModel();
            GoodsModelDTO goodsModel;
            if (modelMap.containsKey(modelKey)) {
                goodsModel = modelMap.get(modelKey);
            } else {
                goodsModel = fetchGoodsModel(shopId, rowInfo);
                modelMap.put(modelKey, goodsModel);
            }
            // 采购明细 放入采购单
            BuyOrderDetail detail = initDetailByModel(rowInfo, goodsModel);
            buyOrder.getDetailList().add(detail);

            successNum++;
        }
        // 保存采购单
        if (errorNum == 0) {
            saveImportOrder(buyOrder);
            msg.append("本次共导入采购单1笔;共有商品采购明细").append(successNum).append("条记录。");
            return R.ok(msg.toString(), "success");
        }
        msg.append("请更正后重新导入。");
        return R.failed(msg.toString());
    }

    /**
     * 一个B客户订单 新增明细
     *
     * @param list
     * @param buyId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> addBuyOrderDetail(List<BuyOrderImport> list, String buyId) {
        StringBuilder msg = new StringBuilder();
        // 查询平台商家店铺
        ShopExtDTO shop = scmSaleBUtils.fetchPurchaseShop("");
        if (null == shop) {
            msg.append("未设置平台商家店铺, 请更正后重新导入。");
            return R.failed(msg.toString());
        }
        // 采购单
        BuyOrder buyOrder = buyOrderService.fetchBySn(buyId);
        if (null == buyOrder) {
            msg.append("订单有误，请更正后重新导入。");
            return R.failed(msg.toString());
        }
        BuyOrderStatusEnum status = buyOrder.getStatus();
        if (status != BuyOrderStatusEnum.submit) {
            msg.append("当前订单状态不可导入。");
            return R.failed(msg.toString());
        }

        String shopId = shop.getId();
        int size = list.size() * 4 / 3 + 1;
        size = Math.max(8, size);
        Map<String, GoodsModelDTO> modelMap = new HashMap<>(size);
        // 导入的明细
        List<BuyOrderDetail> detailList = new ArrayList<>();
        // 处理每行
        int i = 1;
        int errorNum = 0;
        int successNum = 0;
        LocalDateTime deliveryTime = buyOrder.getDeliveryTime().atStartOfDay();
        String buySeq = buyOrder.getBuySeq().getType();
        String buyerName = buyOrder.getBuyerName();
        for (BuyOrderImport rowInfo : list) {
            i++;
            // todo 修改为分组校验
            rowInfo.setDeliveryTime(deliveryTime);
            rowInfo.setBuySeq(buySeq);
            rowInfo.setBuyerName(buyerName);
            // 验证是否空行， 是否填入必须属性;  1 通过  0 空行 -1 缺少必填属性
            R result = checkInfo(rowInfo);
            int flag = result.getCode();
            if (flag == 0) {
                continue;
            }
            if (flag == -1) {
                msg.append("第").append(i).append("行填写有误:").append(result.getMsg()).append("|");
                errorNum++;
                continue;
            }
            // 查找商品 , 不存在时新增至数据库/map
            String modelKey = rowInfo.getGoodsName() + rowInfo.getGoodsModel();
            GoodsModelDTO goodsModel;
            if (modelMap.containsKey(modelKey)) {
                goodsModel = modelMap.get(modelKey);
            } else {
                goodsModel = fetchGoodsModel(shopId, rowInfo);
                modelMap.put(modelKey, goodsModel);
            }
            // 采购明细 放入采购单
            BuyOrderDetail detail = initDetailByModel(rowInfo, goodsModel);
            detailList.add(detail);
            successNum++;
        }

        // 保存采购单
        if (errorNum == 0) {
            saveOrderAndDetail(buyOrder, detailList);
            msg.append("本次共导入商品采购明细").append(successNum).append("条记录。");
            return R.ok(msg.toString(), "success");
        }
        msg.append("请更正后重新导入。");
        return R.failed(msg.toString());
    }

    /**
     * 更新采购单  保存新增明细
     *
     * @param buyOrder
     * @param detailList
     */
    private void saveOrderAndDetail(BuyOrder buyOrder, List<BuyOrderDetail> detailList) {
        // 保存明细  计算金额
        BigDecimal total = buyOrder.getShipFee();
        total = (null == total) ? BigDecimal.ZERO : total;

        // 已有明细
        QueryWrapper<BuyOrderDetail> query = new QueryWrapper<>();
        query.lambda()
                .eq(BuyOrderDetail::getBuyId, buyOrder.getBuyId())
                .ne(BuyOrderDetail::getStatus, BuyOrderStatusEnum.cancel);
        List<BuyOrderDetail> existList = buyOrderDetailService.list(query);
        int size = (existList.size() + detailList.size()) * 4 / 3 + 1;
        size = Math.max(size, 8);
        // 商品去重
        Map<String, BuyOrderDetail> detailMap = new HashMap<>(size);
        for (BuyOrderDetail detail : existList) {
            total = total.add(detail.getBuyPrice().multiply(detail.getOrderGoodsNum()));
            detailMap.put(detail.getGoodsModelId(), detail);
        }

        total = saveBuyOrderDetail(buyOrder, total, detailList, detailMap);

        BuyOrder update = new BuyOrder();
        update.setId(buyOrder.getId());
        update.setGenPrice(total);
        update.setTotalPrice(total);
        update.setStatus(BuyOrderStatusEnum.submit);
        buyOrderService.updateById(update);
    }

    /**
     * 保存商品明细 重复明细合并
     *
     * @param buyOrder
     * @param total
     * @param detailList
     * @param detailMap
     */
    private BigDecimal saveBuyOrderDetail(BuyOrder buyOrder, BigDecimal total, List<BuyOrderDetail> detailList, Map<String, BuyOrderDetail> detailMap) {
        String buyId = buyOrder.getBuyId();
        BuyOrderStatusEnum status = BuyOrderStatusEnum.submit;
        String pmId = buyOrder.getPmId();
        String buyerId = buyOrder.getBuyerId();
        DetailSummaryFlagEnum summaryFlag = DetailSummaryFlagEnum.wait;

        for (BuyOrderDetail detail : detailList) {
            String goodsModelId = detail.getGoodsModelId();
            BigDecimal goodsNum = detail.getOrderGoodsNum();
            total = total.add(detail.getBuyPrice().multiply(goodsNum));
            // 商品重复
            if (detailMap.containsKey(goodsModelId)) {
                BuyOrderDetail update = detailMap.get(goodsModelId);
                // 数量增加 价格以后来的为准
                total = total.add((detail.getBuyPrice().subtract(update.getBuyPrice())).multiply(update.getGoodsNum()));
                update.setBuyPrice(detail.getBuyPrice());

                goodsNum = goodsNum.add(update.getOrderGoodsNum());
                update.setGoodsNum(goodsNum);
                update.setOrderGoodsNum(goodsNum);
                buyOrderDetailService.updateById(update);
                detailMap.put(goodsModelId, update);
            } else {
                // 新增
                detail.setBuyId(buyId);
                detail.setPmId(pmId);
                detail.setBuyerId(buyerId);
                detail.setStatus(status);
                detail.setSummaryFlag(summaryFlag);
                buyOrderDetailService.save(detail);
                detailMap.put(goodsModelId, detail);
            }
        }
        return total;
    }
}
